// 
// Copyright 2010 Classpath Pty Ltd <opensource@classpath.com.au>
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//     http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// 

using build

**
** Tests the `Router` class.
**
internal class RouterTest : Test {

    **
    ** An instance of `Router` for testing.
    **
    Router? router

    **
    ** A list of [Migrations]`Migration` to test routing with.
    **
    Migration[]? migrations

    **
    ** Performs additional setup needed for `RouterTest`.
    **
    override Void setup() {
        super.setup()
        migrations = [RouterMigrationFrom0To1(), RouterMigrationFrom3To4(), RouterMigrationFrom2To3(), RouterMigrationFrom1To2()]
        router = Router(migrations)
    }

    **
    ** Cleans up additional setup needed for `RouterTest`.
    **
    override Void teardown() {
        super.teardown()
        migrations = null
        router = null
    }

    **
    ** Tests `Router.route` from an invalid start point.
    **
    Void testRouteFromInvalid() {
        router.migrations.removeAt(0)
        verifyErr(Err#) {
            router.route(Version("0.0.0"), Version("0.0.4"))
        }
    }

    **
    ** Tests `Router.route` to an invalid end point.
    **
    Void testRouteToInvalid() {
        router.migrations.removeAt(3)
        verifyErr(Err#) {
            router.route(Version("0.0.0"), Version("0.0.4"))
        }
    }

    **
    ** Tests `Router.route` on a route with a missing mid point.
    **
    Void testRouteMissingStep() {
        router.migrations.removeAt(2)
        verifyErr(Err#) {
            router.route(Version("0.0.0"), Version("0.0.4"))
        }
    }

    **
    ** Tests `Router.route` between the same versions.
    **
    Void testRouteSameFromTo() {
        verifyEq([,], router.route(Version("0.0.0"), Version("0.0.0")))
        verifyEq([,], router.route(Version("0.0.1"), Version("0.0.1")))
        verifyEq([,], router.route(Version("0.0.2"), Version("0.0.2")))
        verifyEq([,], router.route(Version("0.0.3"), Version("0.0.3")))
        verifyEq([,], router.route(Version("0.0.4"), Version("0.0.4")))
    }

    **
    ** Tests `Router.route` for an upwards migration from beginning to end.
    **
    Void testValidRouteUp() {
        result := router.route(Version("0.0.0"), Version("0.0.4"))
        verifyEq(4, result.size)
        migrations.each {
            verify(result.contains(it))
        }
        Migration? last
        result.each {
            if (last != null) {
                verifyEq(last.to, it.from)
            }
            last = it
        }
        verifyEq(Version("0.0.0"), result[0].from)
        verifyEq(Version("0.0.4"), result[3].to)
    }

    **
    ** Tests `Router.route` for a downwards migration from end to beginning.
    **
    Void testValidRouteDown() {
        result := router.route(Version("0.0.4"), Version("0.0.0"))
        verifyEq(4, result.size)
        migrations.each {
            verify(result.contains(it))
        }
        Migration? last
        result.each {
            if (last != null) {
                verifyEq(last.from, it.to)
            }
            last = it
        }
        verifyEq(Version("0.0.4"), result[0].to)
        verifyEq(Version("0.0.0"), result[3].from)
    }

    **
    ** Tests `Router.route` for an upwards migration for a subset of all possible steps.
    **
    Void testValidSubRouteUp() {
        result := router.route(Version("0.0.1"), Version("0.0.3"))
        verifyEq(2, result.size)
        // This slice is dependent on the order assigned in setup() - if that changes this test will break
        migrations[-2..-1].each {
            verify(result.contains(it))
        }
        Migration? last
        result.each {
            if (last != null) {
                verifyEq(last.to, it.from)
            }
            last = it
        }
        verifyEq(Version("0.0.1"), result[0].from)
        verifyEq(Version("0.0.3"), result[1].to)
    }

    **
    ** Tests `Router.route` for an downwards migration for a subset of all possible steps.
    **
    Void testValidSubRouteDown() {
        result := router.route(Version("0.0.3"), Version("0.0.1"))
        verifyEq(2, result.size)
        // This slice is dependent on the order assigned in setup() - if that changes this test will break
        migrations[-2..-1].each {
            verify(result.contains(it))
        }
        Migration? last
        result.each {
            if (last != null) {
                verifyEq(last.from, it.to)
            }
            last = it
        }
        verifyEq(Version("0.0.3"), result[0].to)
        verifyEq(Version("0.0.1"), result[1].from)
    }

}

**
** Provides a base class for `RouterTest` migrations.
**
abstract internal class RouterMigration : Migration {

    **
    ** An empty implementation of `Migration.up` for testing.
    **
    override Void up() {}

    **
    ** An empty implementation of `Migration.down` for testing.
    **
    override Void down() {}

}

**
** Provides a `Migration` from `sys::Version` 0.0.0 to `sys::Version` 0.0.1.
**
internal class RouterMigrationFrom0To1 : RouterMigration {

    **
    ** Sets the `Migration.from` to `sys::Version` 0.0.0.
    **
    override Version from := Version("0.0.0")

    **
    ** Sets the `Migration.to` to `sys::Version` 0.0.1.
    **
    override Version to := Version("0.0.1")

}

**
** Provides a `Migration` from `sys::Version` 0.0.1 to `sys::Version` 0.0.2.
**
internal class RouterMigrationFrom1To2 : RouterMigration {

    **
    ** Sets the `Migration.from` to `sys::Version` 0.0.1.
    **
    override Version from := Version("0.0.1")

    **
    ** Sets the `Migration.to` to `sys::Version` 0.0.2.
    **
    override Version to := Version("0.0.2")

}

**
** Provides a `Migration` from `sys::Version` 0.0.2 to `sys::Version` 0.0.3.
**
internal class RouterMigrationFrom2To3 : RouterMigration {

    **
    ** Sets the `Migration.from` to `sys::Version` 0.0.2.
    **
    override Version from := Version("0.0.2")

    **
    ** Sets the `Migration.to` to `sys::Version` 0.0.3.
    **
    override Version to := Version("0.0.3")

}

**
** Provides a `Migration` from `sys::Version` 0.0.3 to `sys::Version` 0.0.4.
**
internal class RouterMigrationFrom3To4 : RouterMigration {

    **
    ** Sets the `Migration.from` to `sys::Version` 0.0.3.
    **
    override Version from := Version("0.0.3")

    **
    ** Sets the `Migration.to` to `sys::Version` 0.0.4.
    **
    override Version to := Version("0.0.4")

}
